package com.tool4j.config;

import com.tool4j.common.Constants;
import com.tool4j.util.JsonUtil;
import com.tool4j.util.http.IPUtil;
import com.tool4j.util.http.Result;
import com.tool4j.util.security.AuthUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * 安全认证过滤器（WebFlux 版）
 */
@Component
@Order(-1)
@Slf4j
public class SecurityWebFilter implements WebFilter {

    @Autowired
    private AuthUtil authUtil;

    @Autowired
    private IPUtil ipUtil;

    private final String[] excludePaths = {
            "/sse/",
            "/auth/sendVerifyCode",
            "/auth/login",
            "/auth/register",
            "/auth/modifyPwd",
            "/auth/weixinEvent",
            "/api/vipCharge/findAll",
            "/static/",
            "/api/info/",
    };

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, CorsConfiguration.ALL);
        if (HttpMethod.OPTIONS.matches(request.getMethod().name())) {
            response.getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, CorsConfiguration.ALL);
            response.getHeaders().add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, CorsConfiguration.ALL);
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }
        String path = request.getURI().getPath();
        // 路径排除判断
        if (isExcluded(path)) {
            return chain.filter(exchange);
        }
        String ip = ipUtil.getIp(request);
        log.info("请求访问：ip={}, region={}, URI={}", ip, ipUtil.searchRegion(ip), path);
        boolean verified = authUtil.verify(exchange);
        if (!verified) {
            return writeUnauthorized(exchange);
        }
        return chain.filter(exchange);
    }

    private boolean isExcluded(String path) {
        for (String exclude : excludePaths) {
            if (exclude.endsWith("/")) {
                if (path.startsWith(exclude)) {
                    return true;
                }
            } else {
                if (path.equals(exclude)) {
                    return true;
                }
            }
        }
        return false;
    }

    private Mono<Void> writeUnauthorized(ServerWebExchange exchange) {
        exchange.getResponse().setStatusCode(HttpStatus.OK);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        byte[] bytes = JsonUtil.obj2JsonString(Result.error(Constants.UN_LOGIN, "认证失败，请重新登录"))
                .getBytes(StandardCharsets.UTF_8);
        return exchange.getResponse().writeWith(
                Mono.just(exchange.getResponse().bufferFactory().wrap(bytes))
        );
    }
}